
package Cluedo.AI;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.Vector;

import Cluedo.API.CluedoConfig;
import Cluedo.API.CluedoListener;
import Cluedo.API.DisproofAlert;
import Cluedo.API.Message;
import Cluedo.API.SuggestionAlert;
import Cluedo.Controller.Controller;
import Cluedo.Game.Card;
import Cluedo.Game.CardCollection;
import Cluedo.Game.Map;
import Cluedo.Game.Player;
import Cluedo.Game.Position;
import Cluedo.Game.Room;
import Cluedo.Game.Square;
import Cluedo.Game.StartingSquare;

import com.cluedo.CluedoDataPad;

/**
 * AI interacts with the game by listening to messages from the GameController and then sending
 * messages back to make changes.
 * <p>
 * 
 * First Created on Dec 12, 2004
 * 
 * @author Alex Ellis
 * 
 */
public class ComplexAI implements
        Runnable, Cluedo.API.AIContract {

    static int sleepPeriod = 10;

    int playerNumTurns = 0;

    CluedoDataPad cDataPad = null;

    HashMap<String, Integer> playerMapping;

    Player me;

    /**
     * This identifies the AI, so it can decide when it's his turn to respond to input.
     */
    String myName = "";

    /**
     * If set to true the AI's run() function will end.
     */
    boolean dead = false;

    /**
     * Stores all incoming messages in a queue. This AI is a thread, therefore it receives messages
     * into a queue and parses them from its run() function.
     */
    Vector inbox = new Vector();

    /**
     * Current behaviour of the AI
     */
    String motive = "";

    /**
     * Detective if we were moved by a suggest
     * 
     */
    Position lastPos = null;

    /**
     * Indicates a disproof to the AI's suggestion was received.
     */
    boolean disproofReceived = true;

    /**
     * Used to store a disproof to the AI's suggestion.
     */
    Message incomingDisproof = null;

    /**
     * List of motives that an AI may take.
     */
    String[] motives = {
            "findRoom", "walkToRoom",
    };

    /**
     * CluedoListener acts as an inbox to receive messages from the GameController and will decide
     * whether it needs to update any information.
     *
     */
    CluedoListener cluedoListener = new CluedoListener() {
        /**
         * Messages here are received from the TextHandler and result in some action being taken
         * locally in this View, commands called from here are located in an inner-class called
         * LocalEvents.<br>
         * The return value from the local event is outputted to the user.
         *
         */
        public void localMessage(Cluedo.API.Message message) {
        }

        /**
         * Receives messages from the GameController and handles them in GameEvents
         *
         */
        public void notifyAlert(Message message) {
            if (debugLevel >= 2) {
                outputMessage("[SmartSimpleAI:notifyAlert] [" + message.type + "] '" + message.data
                        + "'\n");
            }
            // One case where we need a little help..
            if (disproofReceived == false
                    && message.type.equals("disproofAlert")) {
                disproofReceived = true;
                incomingDisproof = message;
                inbox.add(message);
            }
            else {
                inbox.add(message);
            }
        }

        /*
         * End code that handles the interaction of the GUI
         */
        public CluedoListener getCluedoListener() {
            return this;
        }

    };

    public CluedoListener getCluedoListener() {
        return cluedoListener;
    }

    private void sleepFor(int interval) {
        try {
            Thread.sleep(interval);
            outputMessage("\nPausing execution.. (" + interval + ") ms\n");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Construct a new simple AI, and bind itself to the Controller to receive game messages.
     *
     * @param args Does nothing
     * @param gameController2 A pointer/link to the Controller.
     * @param AIName Name that uniquely identifes this AI player
     */
    public ComplexAI(String args, Controller gameController, String AIName) {
        // super(args);
        myName = AIName;
        this.gameController = gameController;
        System.out.println("[SmartSimpleAI:] GC=" + this.gameController.toString() + ", NAME="
                + myName);
        bindAI();
        motive = "findRoom";
    }

    /**
     * AI will bind its CluedoListener to the gameController such that it will receive game
     * messages.
     */
    public void bindAI() {
        System.out.println("[SmartSimpleAI2:BindAI] binding to listener " + getCluedoListener());
        try {
            gameController.bindListener(getCluedoListener());
        } catch (Exception Error) {
            System.out.println("[SmartSimpleAI2:BindAI] can't bind");
        }
    }

    /**
     * If called thread will exit run() method. Called when game ends.
     */
    synchronized public void die() {
        dead = true;
    }

    /**
     * Main AI loop, will stop if die() is called.
     *
     * Removes message from inbox and processes it in handleMessage
     *
     */
    public void run() {
        while (dead == false) {
            try {
                sleepFor(100);

                if (inbox.size() > 0) {
                    while (inbox.size() > 0) {
                        Thread.sleep(10);
                        try {
                            handleMessage(((Message) inbox.firstElement()));
                        } catch (Exception unknown) {
                            unknown.printStackTrace();
                        }
                        inbox.remove(inbox.firstElement());
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    boolean printedMyCards = false;

    /**
     * A message is taken from the queue and given to the AI, he decides what to do about that.
     * 
     * @param message Message from the Q.
     *
     */
    public synchronized void handleMessage(Message message) {
        String output = "";

        // Initializing once
        if (printedMyCards == false)
        {
            // TODO
            me = gameController.getBoard().getPlayer(myName);
            CardCollection c = me.getCards();
            ArrayList<String> cards = new ArrayList<String>();
            for (int i = 0; i < c.countCards(); i++) {
                cards.add(((Card) c.getCards().elementAt(i)).getName());
            }

            Vector<Player> players = gameController.getBoard().getPlayers();
            playerMapping = new HashMap<String, Integer>(players.size());

            for (int i = 0; i < players.size(); i++) {
                playerMapping.put(players.get(i).getRealName(), i);
            }
            cDataPad = CluedoDataPad.newInstance(false, playerMapping.get(myName),
                    players.size(), cards);
            // TODO REGISTER PROBABILITY CHANGE
            updateRegistryData(me);

            System.out.println(myName + "\n"
                    + gameController.getBoard().getPlayer(myName).getCards().toXML());
            printedMyCards = true;
        }

        if (debugLevel > 5)
            outputMessage("handleMessage " + message.type);
        if (gameController.getBoard().getGameFinished() == false) {

            if (message.type.equals("newRoll")) {

            }
            if (message.type.equals("commenceGame")) {
                message.type = "nextPlayer";
                outputMessage("We're all waiting a little bit for the game code to warm-up");
                sleepFor(sleepPeriod * 10);

            }
            else if (message.type.equals("newEnvelope")) {

            }
            else if (message.type.equals("suggestionAlert")
                    && message.data.equals(myName)) {
                handleSuggestionAlert(message);
            }
            else if (message.type.equals("disproofAlert")) {
                outputMessage("\nHeard a disproofAlert ...\n");
                DisproofAlert disproofAlert = (DisproofAlert) message.getParameters().get(0);
                if (disproofAlert.originalSuggestionAlert.suggestedBy.equals(myName)) {
                    updatePlayer(disproofAlert);
                } else {
                    updatePlayerSoftEvidence(disproofAlert, disproofAlert.disprovedBy);
                }
                disproofReceived = true;
            }
            else if (message.type.equals("winGame")) {
                outputMessage("Game ends.", 1);
                dead = true;
                return;
            }
            else if (message.type.equals("gameOver")) {
                outputMessage("Game ends.", 1);
                dead = true;
                return;
            }
            if (message.type.equals("nextPlayer")) {

                if (gameController.getBoard().getCurrentPlayer().getRealName().equals(myName)) {
                    // outputMessage(buildCards());
                    outputMessage(" ********************************************");
                    outputMessage(" **** "
                            + gameController.getBoard().getCurrentPlayer().getRealName() + " ****");
                    outputMessage(" ********************************************");
                    output = "\n";

                    try {
                        takeTurn();
                    } catch (Exception exp) {
                        exp.printStackTrace();
                    }
                    // outputMessage(buildCards());
                }
            }
            else if (message.type.equals("moveAlert")) {

            }
        }
        else if (dead == false) {
            outputMessage("Game is over and i'm quitting..");
            System.out.println(cDataPad.getGridLog());
            System.out.println("Turns Taken " + playerNumTurns);
            die();
        }
        if (!output.equals("")) {
            outputMessage(output);
        }
    }

    /**
     * @param string
     * @param i
     */
    private void outputMessage(String output, int i) {
        if (i == 1) {
            StringTokenizer lines = new StringTokenizer(output, "\n");
            while (lines.hasMoreElements() == true) {
                System.err.println("[AI:" + myName + "] " + lines.nextToken());
            }
            System.err.println();
        }
    }

    /**
     * Manually set the AI name, see constructor
     */
    public void setMyName(String myName) {
        this.myName = myName;
    }

    /**
     * Only prints a debug
     */
    public void initaliseAI() {
        System.out.println("[SmartSimpleAI2:initaliseAI] Initalised " + myName + ".");
    }

    /**
     * Start code that handles the interaction of the AI
     * 
     */

    /** The game controller for this View */
    Controller gameController = null;
    /** The parser for text input from this View */
    Cluedo.GUI.TextParser textParser;
    /** The handler for input from the parser on this View */
    Cluedo.GUI.TextEventHandler textHandler = new Cluedo.GUI.TextEventHandler(this);
    /** Create a game config class to read in and sort out what the new game will be like */
    CluedoConfig gameConfig = null;
    int debugLevel = 0;

    /**
     * Debug function makes clear which AI is "talking"
     */
    synchronized void outputMessage(String output) {
        StringTokenizer lines = new StringTokenizer(output, "\n");
        while (lines.hasMoreElements() == true) {
            System.out.println("[AI:" + myName + "] " + lines.nextToken());
        }
        System.out.println();
    }

    /**
     * Figures out what to do with a message of type ='suggestionAlert'.
     * <p>
     *
     * Searches cards in suggestionAlert until it finds one it has in its set of cards, then
     * composes disproofAlert alert as a reply to the message and relays this through the
     * controller.
     *
     */
    private void handleSuggestionAlert(Message message) {

        outputMessage("\nHeard a suggestionAlert directed at me...\n");
        Message disproofAlert = new Message();
        String chosenCard = "";

        disproofAlert.type = "disproofAlert";

        SuggestionAlert alert = (SuggestionAlert) message.parameters.elementAt(0);
        Player me = gameController.getBoard().getPlayer(myName);

        if (me.getCards().containsCard(alert.getWhat()))
            chosenCard = alert.what.getName();
        else if (me.getCards().containsCard(alert.getWhere()))
            chosenCard = alert.where.getName();
        else if (me.getCards().containsCard(alert.getWho()))
            chosenCard = alert.who.getName();

        // If this happens, there is a bug in the system.
        if (chosenCard.equals(""))
            outputMessage("I didn't have a card to disprove with and yet I was asked to disprove!");

        // DisproofAlert dalert = new DisproofAlert(chosenCard, alert);
        DisproofAlert dalert = new DisproofAlert(chosenCard, alert, myName);
        disproofAlert.parameters.add(dalert);

        gameController.relayMessage(disproofAlert);

        outputMessage("\nRelay of disproofAlert... Ok\n");

    }

    /**
     * Clean up anything and notify gameController to allow next player to play.
     *
     */
    synchronized public String endGo() throws Exception {
        String output = "";
        Vector empty = new Vector();

        gameController.directCommand("nextPlayer", empty);

        return output;
    }

    /**
     * Function for debug, returns string status of all cards.
     *
     * @return String containing status of all cards.
     */
    synchronized public String buildCards() {
        StringBuffer buffer = new StringBuffer();

        Player me = gameController.getBoard().getPlayer(myName);
        CardCollection myCards = me.getCards();
        CardCollection myDCards = me.getDetectivePad().getCardList();

        for (int i = 0; i < myDCards.countCards(); i++) {
            buffer.append("- " + myDCards.cardAt(i).getName() + "\t"
                    + myDCards.cardAt(i).isEliminated() + "\n");
        }

        return buffer.toString();
    }

    /**
     * Called if after a nextPlayer alert if getBoard().getCurrentPlayer().getRealName() == me
     * <p>
     *
     * AI player will decide what he needs to do on this turn.
     *
     */
    synchronized public void takeTurn() throws Exception {
        String output = "";
        boolean deadlock = inDeadlock();
        playerNumTurns++;
        outputMessage("takeTurn..");

        /** Firstly make a small thinking pause */
        sleepFor(sleepPeriod);

        /** Get the bits we need for the turn. */
        Player me = gameController.getBoard().getPlayer(myName);
        CardCollection myCards = me.getCards();
        CardCollection myDCards = me.getDetectivePad().getCardList();

        System.out.println("\t\tinDeadlock() " + inDeadlock());

        /**
         * Check if an accusation should be made.
         *
         */
        if (shouldAccuse() == true) {
            boolean wonGame = makeAccusation();
            System.out.println("COMPLEX accuse");
            if (wonGame == true) {
                Vector arguments = new Vector();
                arguments.add(me);
                gameController.directCommand("winGame", arguments);
            }
            else {
                gameController.directCommand("removeInteractive", null);
            }

            System.out.println("My accusation was " + wonGame);

            dead = true;
            return;
        }

        output += "I have to eliminate " + myDCards.getUneliminatedCards().size() + " / ("
                + myDCards.countCards() + ") cards.\n";

        outputMessage(output);

        /**
         * If we get moved on the last turn then we have to find the nearest room again
         */
        /*
         * if(lastPos != null && (motive.equals("walkToRoom") && lastPos != me.getPosition())) {
         * motive = "findRoom"; System.out.println("I got moved last turn."); }
         */

        /**
         * If not in the process of moving to a room, find a room that we want to visit.
         */
        if (motive.equals("findRoom")) {
            System.out.println("COMPLEX findRoom()");
            destination = findRoom();
            System.out.println("Finding room: FinalDestination=" + finalDestination
                    + "\tdestination=" + destination);
            motive = "walkToRoom";
        }

        sleepFor(sleepPeriod);

        /**
         * If I'm supposed to find my way to the room then I'll start doing that.
         * 
         */
        if (deadlock == false) {
            if (motive.equals("walkToRoom")) {
                if (destination == null || destination.equals(me.getPosition())) {
                    System.out.println("This current pos is the nearest!?");
                    destination = findFreeNeighbour(me.getPosition());
                }
                if (destination != null) {
                    walkToRoom(destination);
                }
                else {
                    motive = "findRoom";
                }
            }
        }

        sleepFor(sleepPeriod);

        /**
         * If it's possible to make a suggestion after the turn, then do so.
         *
         */
        if (shouldSuggest() == true) {
            System.out.println("COMPLEX shouldSuggest()");
            output += suggest();
        }

        if (destination == me.getPosition()) {
            outputMessage("Arrvied at destination");
            motive = "findRoom";

        }
        else {
            outputMessage("Still in transit");
        }

        // lastPos = me.getPosition();

        /**
         * Now end the go and let the next player have his go.
         */
        try {
            outputMessage("calling endGo()");
            endGo();
            outputMessage("endGo() returned");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * Decides if the AI will make a suggestion where it is.
     *
     * Added mainly to disable conspiracy.
     * 
     * @return true if ok, false if not ok
     */
    public boolean shouldSuggest() {
        boolean ok = true;
        Player me = gameController.getBoard().getCurrentPlayer();
        Position p = me.getPosition();

        if (me.getCards().containsCard(new Card(p.getID(), "Room")) == true)
            ok = false;

        if (me.canSuggestHere() == false || me.canSuggest() == false)
            ok = false;

        return ok;
    }

    /**
     * Check for a free position in one of the neighbouring nodes.
     *
     * @return null if node doesn't have free neighbouring node, otherwise return first found node
     */
    Position findFreeNeighbour(Position node) {
        Position found = null;

        for (int i = 0; i < node.getNeighbours().size(); i++) {
            if (((node.getNeighbours().elementAt(i)) instanceof Room == true) ||
                    ((Position) node.getNeighbours().elementAt(i)).isOccupied() == false) {
                found = (Position) node.getNeighbours().elementAt(i);
            }
        }

        return found;
    }

    /**
     * Walk to destination.
     * <p>
     * 
     * Try to get to the destination by whatever means neccessary, make suggestions along the way
     * wherever permitted.
     * 
     */
    synchronized public void walkToRoom(Position destination) {
        String walkInfo = "";
        String output = "";

        try {
            Player me = gameController.getBoard().getPlayer(myName);
            CardCollection myCards = me.getDetectivePad().getCardList();

            System.out.println("walkToRoom(" + destination.getID() + ") at: "
                    + me.getPosition().getID());

            // System.out.println("Before dice");

            int diceRoll = 0;

            /**
             * Call the roll-dice function to find out how many squares we can move
             */
            try {
                diceRoll = rollDice();
            } catch (Exception e) {
                e.printStackTrace();
            }

            // System.out.println("After dice");
            sleepFor(sleepPeriod);

            System.out.println("+ Dice: " + diceRoll);

            /**
             * Check if there is a legal path to the route
             */
            Vector legalRoute = gameController.getBoard().map
                    .routeTo(me.getPosition(), destination);

            Vector illegalRoute = gameController.getBoard().map.illegalRouteTo(me.getPosition(),
                    destination);

            Vector chosenRoute = new Vector();

            boolean wholePath = false;
            boolean legal = false;
            int roll = me.getMovesRemaining();

            int movesOnPath = 0;

            outputMessage("Illegal size: "
                    + gameController.getBoard().map.illegalRouteTo(me.getPosition(), destination)
                            .size() + "\n");
            outputMessage("Legal size: "
                    + gameController.getBoard().map.routeTo(me.getPosition(), destination).size()
                    + "\n");

            /** If we can use special rule */
            if ((gameController.getBoard().getGameType() == 1 && roll == 12 && destination instanceof Room)) {
                chosenRoute = new Vector();
                chosenRoute.add(me.getPosition());
                chosenRoute.add(destination);

                legal = true;
                wholePath = true;
            }
            /** Can take a legal route in one go? ? */
            else if ((legalRoute.size() <= roll) && legalRoute.size() > 0) {
                chosenRoute = legalRoute;
                wholePath = true;
            }
            /** If we only move part of the way along */
            else {
                int movesOnLegalPath = 0;
                int movesOnIllegalPath = 0;

                if (legalRoute.size() > 0) {
                    movesOnLegalPath = validatePath(legalRoute, roll);
                    System.err.println("\nMY PLACE = " + movesOnLegalPath + "\n");
                }

                movesOnIllegalPath = validatePath(illegalRoute, roll);

                try {
                    System.err.println("\tmovesOnLegalPath " + movesOnLegalPath
                            + " movesOnIllegalPath " + movesOnIllegalPath);
                } catch (Exception e) {
                    e.printStackTrace();
                }

                if (movesOnLegalPath >= movesOnIllegalPath) {
                    chosenRoute = legalRoute;
                    movesOnPath = movesOnLegalPath;
                    System.err.println("LegalRoute " + movesOnPath);
                }
                else {
                    chosenRoute = illegalRoute;
                    // "Chosen: " + chosenRoute + " illegal: " + illegalRoute);
                    movesOnPath = movesOnIllegalPath - 1;
                    System.err.println("IllegalRoute " + movesOnPath);
                }

            }

            System.out.println("Chosen route size: " + chosenRoute.size());

            Position stopPoint = null;
            prevPosition = stopPoint;
            if (wholePath == true) {
                stopPoint = ((Position) chosenRoute.lastElement());
            }
            else {
                System.out.println(movesOnPath + " route: " + chosenRoute);
                stopPoint = ((Position) chosenRoute.get(movesOnPath - 1));
            }

            System.err.println("Moving to " + stopPoint.getID() + " " + chosenRoute + " wholePath "
                    + wholePath + " m=" + movesOnPath);

            if (movesOnPath >= 1 || wholePath == true) {
                movePlayer(stopPoint, chosenRoute);
            }
            else {
                System.err.println("AI Confused");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param chosenRoute
     * @return
     */
    private int validatePath(Vector chosenRoute, int rollAllowed) {
        int movesOnPath = 1;
        boolean wholePath = false;

        for (int i = 1; i < chosenRoute.size(); i++) {

            // Can't pass through positions
            if (((Position) chosenRoute.get(i)).isOccupied() == true &&
                    ((Position) chosenRoute.get(i)) instanceof Square) {
                wholePath = false;
                System.err.println("Break 1");
                break;
            }

            // Stop in a room
            if (((Position) chosenRoute.get(i)) instanceof Room) {
                wholePath = false;
                System.err.println("Break 2 " + ((Position) chosenRoute.get(i)).getID() + " "
                        + movesOnPath);

                // TODO: movesOnPath++;
                movesOnPath++;

                break;
            }

            // If the dice roll has been exceeded
            if (i >= rollAllowed) {
                wholePath = false;
                System.err.println("Break 3");
                break;
            }

            System.err.println("movesOnPath: " + movesOnPath);
            movesOnPath++;
        }

        return movesOnPath;
    }

    // PETER : find the final goal room
    // PETER : must consider it again.
    // USE the probability
    synchronized public Position findFinalRoom()
    {
        Vector rooms = gameController.getBoard().map.getNodes("rooms");
        // /// PETER findRoom.
        try {
            Player me = gameController.getBoard().getPlayer(myName);
            CardCollection myDCards = me.getDetectivePad().getCardList();

            // Vector<Position> ran = new Vector<Position>(); //For test

            // Rooms to search
            Vector possibleCardList = new Vector();

            Double smallProb = (double) -1000;
            Room target = null;
            int p = 0;
            for (int i = 0; i < rooms.size(); i++)
                if (myDCards.cardByName(((Room) rooms.get(i)).getID()).isEliminated() == false)
                {
                    // TODO Integrate probability count from data
                    // Choose higher probability
                    if (myDCards.cardByName(((Room) rooms.get(i)).getID()).getProbability() > smallProb)
                    {
                        smallProb = myDCards.cardByName(((Room) rooms.get(i)).getID())
                                .getProbability();
                        finalDestination = (Room) rooms.get(i);
                    }
                    possibleCardList.add(myDCards.cardByName(((Room) rooms.get(i)).getID()));
                    // ran.add((Room)rooms.get(i)); //For test
                }

            // finalDestination = ran.get((int) ((Math.random()*ran.size()) %ran.size())); //For
            // test
        } catch (Exception e) {
            e.printStackTrace();
        }

        return finalDestination;
    }

    // PETER : DONE. Verified. Working correctly
    public int distanceInTurn(Position From, Position To)
    {
        if (From == null || To == null)
            return 0;
        if (From == To)
            return 0;

        // RouteTo(From, To) function is verified. If we ask Lounge to Conservatory, it returns
        // Lounge to Conservatory.
        Vector movePath = new Vector();
        movePath = gameController.getBoard().map.routeTo(From, To);

        return (int) Math.ceil((double) (movePath.size() - 1) / 3.5);
    }

    // Peter
    public class DATA
    {
        Position parent;
        Position current;
        int cost;

        DATA(Position parent, Position current, int cost)
        {
            this.parent = parent;
            this.current = current;
            this.cost = cost;
        }
    }

    // PETER : rebuild the path (direction in returning vector: goal -> start)
    synchronized public Vector<Position> rebuildPath(Vector<DATA> list)
    {
        Vector<Position> ret = new Vector<Position>();

        if (list.isEmpty())
            return null;

        DATA pivot = list.lastElement();

        while (pivot.parent != pivot.current)
        {
            ret.add(pivot.current);
            int cost = Integer.MAX_VALUE;
            int p = 0;
            for (int i = 0; i < list.size(); i++)
            {
                if (list.get(i).current == pivot.parent)
                {
                    if (list.get(i).cost < cost)
                    {
                        p = i;
                        cost = list.get(i).cost;
                    }
                }
            }
            pivot = list.get(p);
        }

        return ret;
    }

    // PETER : get the minimum cost
    synchronized public int minimumCostIter(Vector<DATA> list, Position To)
    {
        int pos = 0, cost = Integer.MAX_VALUE;
        for (int i = 0; i < list.size(); i++)
        {
            int h = distanceInTurn(list.get(i).current, finalDestination);
            if (h < cost)
            {
                pos = i;
                cost = h;
            }
        }
        return pos;
    }

    // PETER
    synchronized public DATA retrieveDataFromList(Vector<DATA> list, DATA p)
    {
        for (int i = 0; i < list.size(); i++)
        {
            if (list.get(i).current == p.current)
                return list.get(i);
        }
        return null;
    }

    // PETER : DONE. Verified. Working correctly
    synchronized public Vector<DATA> adjacentRoomsList(Position From)
    {
        Vector<DATA> ret = new Vector<DATA>();
        Player current = gameController.getBoard().getPlayer(myName);

        Vector rooms = gameController.getBoard().map.getNodes("rooms");

        for (int i = 0; i < matrix.size(); i++)
        {
            if (matrix.get(i).checkIsThisAdjacent(From) == true)
                ret.add(new DATA(null, matrix.get(i).me, distanceInTurn(matrix.get(i).me,
                        finalDestination)));
        }

        return ret;
    }

    // PETER : A* Algorithm with the heuristic function based on turns (turn = distance / 3.5 where
    // 3.5 is the average of a dice)
    synchronized public Vector<Position> AStar(Position From, Position To, int cost)
    {
        if (From == To)
        {
            System.out
                    .println("[Critical Error][In Astar() Method] You are trying to find a room you belong to / I will return null");
            return null;
        }
        Vector<DATA> closed = new Vector<DATA>();
        Vector<DATA> open = new Vector<DATA>();
        closed.clear();
        open.clear();

        open.add(new DATA(From, From, cost)); // Initialize

        int currentcost = cost;
        Position curPos = From;

        while (open.isEmpty() == false)
        {
            // remove from OPEN the node <n,p> with minimum f(n) // n -> current
            int minimumIter = minimumCostIter(open, To);
            DATA n = open.get(minimumIter);
            open.remove(minimumIter);

            // CLOSED.add(<n,p>);
            n.parent = curPos;
            closed.add(n);
            curPos = n.current;

            // if( n == goal) return buildPath(p); // rebuild the path from ret variable.
            if (n.current == To)
            {
                Vector<Position> ret = rebuildPath(closed);
                closed.clear();
                open.clear();
                return ret;
            }
            // for(each edge connecting n&m with cost c)
            Vector<DATA> adjacentRooms = adjacentRoomsList(n.current);
            for (int i = 0; i < adjacentRooms.size(); i++) // m = adjacent room. m is the iterator.
            {
                DATA checkClosed = retrieveDataFromList(closed, adjacentRooms.get(i)); // valid
                                                                                       // parent, m,
                                                                                       // q
                DATA checkOpen = retrieveDataFromList(open, adjacentRooms.get(i)); // invalid
                                                                                   // parent, m, q

                int pe = distanceInTurn(curPos, adjacentRooms.get(i).current); // cost{p|e}

                // if(CLOSED has <m,q> && cost{p|e} < q)
                if (checkClosed != null && pe < checkClosed.cost)
                {
                    // CLOSED.remove(n)
                    closed.remove(n);
                    // OPEN.add(<m, {p|e}>)
                    open.add(new DATA(null, checkClosed.current, pe));
                }
                // else if ( OPEN has <m,q> && cost{p|e} < q)
                else if (checkOpen != null && pe < checkOpen.cost)
                {
                    // q = {p|e}
                    for (int j = 0; j < open.size(); j++)
                    {
                        if (open.get(j) == checkOpen)
                        {
                            open.get(j).cost = pe;
                            open.get(j).parent = adjacentRooms.get(i).parent;
                        }
                    }
                }
                // else if(OPEN has not m)
                else if (checkOpen == null)
                {
                    // then put <m,{p|e}> on OPEN
                    open.add(new DATA(null, adjacentRooms.get(i).current, pe));
                }
            }
        }

        closed.clear();
        open.clear();
        return null; // failed to find the path
    }

    // PETER : DONE. Verified. Working correctly
    class RoomMatrix
    {
        String name;
        Position me;
        Vector<Room> adjacentList = new Vector<Room>();
        char x1, x2;
        int y1, y2;

        RoomMatrix(String name, Position me, Vector<Room> list, String point1, String point2)
        {
            this.name = name;
            this.me = me;
            x1 = point1.charAt(0);
            x2 = point2.charAt(0);
            if (point1.length() == 3)
                y1 = Integer.parseInt(point1.substring(1, 3));
            else
                y1 = point1.charAt(1) - '0';
            if (point2.length() == 3)
                y2 = Integer.parseInt(point2.substring(1, 3));
            else
                y2 = point2.charAt(1) - '0';

            for (int i = 0; i < list.size(); i++)
            {
                adjacentList.add(list.get(i));
            }
        }

        boolean checkIsThisAdjacent(Position pos)
        {
            String loc = pos.getID();
            if (loc.length() <= 3) // check
            {
                char x = loc.charAt(0);
                int y;
                if (loc.length() == 3)
                    y = Integer.parseInt(loc.substring(1, 3));
                else
                    y = loc.codePointAt(1) - '0';

                if (x1 <= x && x <= x2 && y1 <= y && y <= y2)
                {
                    // System.out.println("Searching Position = " + name + "\t\tMyposition = " + loc
                    // + "\t ADJACENT = TRUE");
                    return true;
                }
                else
                {
                    // System.out.println("Searching Position = " + name + "\tMyposition = " + loc +
                    // "\t ADJACENT = FALSE");
                    return false;
                }
            }

            if (adjacentList.contains(pos))
                return true;
            else
                return false;
        }
    }

    Vector<RoomMatrix> matrix;
    Vector<Position> myRoute = null;
    Position prevPosition = null;
    Position destination = null;
    Position finalDestination = null;

    synchronized public Position findRoom()
    {
        // /// PETER findRoom.
        // /// PETER we should find the route to the finalDestination
        // if(finalDestination == null) return null;

        /*
         * PETER
         * 
         * Simple Algorithm. 1. int a = DistanceInTurn(current, goal) 2. int b =
         * DistanceInTurn(closest room, goal) 3. if(a > b+DistanceInTurn(current, closest room) then
         * go to the closest room else go to the goal directly
         * 
         * Use A* The heuristic distance function: int DistanceInTurn(source, destination) Define A*
         * Function: Vector<Position> AStar(Position From, Position To, int cost)
         */

        Player me = gameController.getBoard().getPlayer(myName);
        Vector allrooms = gameController.getBoard().map.getNodes("rooms");

        if (matrix == null) // PETER : DONE. Verified. Working correctly
        {
            matrix = new Vector<RoomMatrix>();
            /*
             * // PETER : DONE. Verified. Working correctly 0 Lounge - (Conservatory, DiningRoom,
             * Hall) / A1 - J9 1 Hall - (Study, Lounge, DiningRoom) / G1-R10 2 Library - (Study,
             * BilliardRoom) / O4-X14 3 Conservatory - (Lounge, Ballroom, BilliardRoom) / P17-X24 4
             * Kitchen - (Study, Ballroom, DiningRoom) / A16-I24 5 BilliardRoom - (Conservatory,
             * Ballroom, Library) / P10-X20 6 Ballroom - (Kitchen, Conservatory, DiningRoom) /
             * F15-S24 7 DiningRoom - (Lounge, Hall, Ballroom) / A7-K18 8 Study - (Kitchen, Hall,
             * Library) / O1-X7
             * 
             * System.out.println("-----------------------"); for(int i=0;i<allrooms.size();i++)
             * System.out.println(((Room)allrooms.get(i)).getID());
             * System.out.println("-----------------------");
             */
            Vector<Room> Lounge = new Vector<Room>();
            Lounge.add((Room) allrooms.get(3));
            Lounge.add((Room) allrooms.get(7));
            Lounge.add((Room) allrooms.get(1));
            matrix.add(new RoomMatrix("Lounge", (Room) allrooms.get(0), Lounge, "A1", "J9"));

            Vector<Room> Hall = new Vector<Room>();
            Hall.add((Room) allrooms.get(8));
            Hall.add((Room) allrooms.get(0));
            Hall.add((Room) allrooms.get(7));
            matrix.add(new RoomMatrix("Hall", (Room) allrooms.get(1), Hall, "G1", "R10"));

            Vector<Room> Library = new Vector<Room>();
            Library.add((Room) allrooms.get(8));
            Library.add((Room) allrooms.get(5));
            matrix.add(new RoomMatrix("Library", (Room) allrooms.get(2), Library, "O4", "X14"));

            Vector<Room> Conservatory = new Vector<Room>();
            Conservatory.add((Room) allrooms.get(0));
            Conservatory.add((Room) allrooms.get(6));
            Conservatory.add((Room) allrooms.get(5));
            matrix.add(new RoomMatrix("Conservatory", (Room) allrooms.get(3), Conservatory, "P17",
                    "X24"));

            Vector<Room> Kitchen = new Vector<Room>();
            Kitchen.add((Room) allrooms.get(8));
            Kitchen.add((Room) allrooms.get(6));
            Kitchen.add((Room) allrooms.get(7));
            matrix.add(new RoomMatrix("Kitchen", (Room) allrooms.get(4), Kitchen, "A16", "I24"));

            Vector<Room> BilliardRoom = new Vector<Room>();
            BilliardRoom.add((Room) allrooms.get(3));
            BilliardRoom.add((Room) allrooms.get(6));
            BilliardRoom.add((Room) allrooms.get(2));
            matrix.add(new RoomMatrix("BilliardRoom", (Room) allrooms.get(5), BilliardRoom, "P10",
                    "X20"));

            Vector<Room> Ballroom = new Vector<Room>();
            Ballroom.add((Room) allrooms.get(4));
            Ballroom.add((Room) allrooms.get(3));
            Ballroom.add((Room) allrooms.get(7));
            matrix.add(new RoomMatrix("Ballroom", (Room) allrooms.get(6), Ballroom, "F15", "S24"));

            Vector<Room> DiningRoom = new Vector<Room>();
            DiningRoom.add((Room) allrooms.get(0));
            DiningRoom.add((Room) allrooms.get(1));
            DiningRoom.add((Room) allrooms.get(6));
            matrix.add(new RoomMatrix("DiningRoom", (Room) allrooms.get(7), DiningRoom, "A7", "K18"));

            Vector<Room> Study = new Vector<Room>();
            Study.add((Room) allrooms.get(4));
            Study.add((Room) allrooms.get(1));
            Study.add((Room) allrooms.get(2));
            matrix.add(new RoomMatrix("Study", (Room) allrooms.get(8), Study, "O1", "X7"));
        } // PETER : DONE. Verified. Working correctly

        // PETER: finalDestination indicates somewhere but not a room. then initialize it.
        /*
         * boolean init = true; if(finalDestination != null) { int i=0; for(i=0; i<allrooms.size();
         * i++) if(allrooms.get(i) == finalDestination) break; if(i != allrooms.size()) init =
         * false; }
         */

        // Only if someone moved my character and I just arrived at the finalDestination, we will
        // get a new route
        // if (prevPosition != me.getPosition() || myRoute == null
        // || (myRoute != null && myRoute.isEmpty() == true) || finalDestination == null)// ||
        // init
        // ==
        // false)
        // {
        finalDestination = findFinalRoom();
        System.out.println("FINAL DESTINATION = " + finalDestination);

        // PETER : I'm currently on the final room tile. Thus I got null pointer
        if (finalDestination == null || finalDestination == me.getPosition())
        {
            System.out.println("ARRIVED AT THE FINALDESTINATION");
            destination = findFreeNeighbour(me.getPosition());
            return destination;
        }
        System.out.println("A* Algorithm Start");
        System.out.println("myPosition = " + me.getPosition().getID() + "\tfinalDestination = "
                + finalDestination.getID());
        Vector<Position> ret = AStar(me.getPosition(), finalDestination, 0);
        System.out.println("A* Algorithm Finished");
        // if(ret.isEmpty() == true) return null;
        myRoute = ret;
        if (ret != null)
        {
            System.out.println("A* Algorithm Result: From Goal");
            for (int i = 0; i < ret.size(); i++)
                System.out.println(ret.get(i).getID());
            System.out.println("A* Algorithm Result: To Start");
        }

        destination = myRoute.lastElement();
        myRoute.remove(destination);
        // }

        // PETER : I arrived at the middle point
        if (destination == me.getPosition())
        {
            destination = myRoute.lastElement();
            myRoute.remove(destination);
            if (myRoute.size() == 0)
                myRoute = null;
        }

        if (gameController.getBoard().map.routeTo(me.getPosition(), destination).size() == 0
                &&
                gameController.getBoard().map.illegalRouteTo(me.getPosition(), destination).size() == 0)
        {
            System.out.println("AI not allowed to move.");
            return null;
        }
        return destination;
    }

    /**
     * @param roomsToSearch
     * @return
     */
    private int[] findDistances(Vector roomsToSearch) {
        int rooms[] = new int[roomsToSearch.size()];

        Player me = gameController.getBoard().getCurrentPlayer();
        Position src = gameController.getBoard().getCurrentPlayer().getPosition();
        Map map = gameController.getBoard().map;

        for (int i = 0; i < roomsToSearch.size(); i++) {
            int path = map.illegalRouteTo(src, (Position) roomsToSearch.get(i)).size();
            rooms[i] = path;
        }

        return rooms;
    }

    /**
     * Finds if the AI should make an accusation.
     * 
     * Makes an accusation if there is 1 weapon, suspect, and room left uneliminated.
     * 
     * This method can be bypassed if a suggestion is not disproved and the player has none of the
     * cards, this means inherently that the player has found the secret cards.
     * 
     * @return accusation decision
     */
    synchronized public boolean shouldAccuse() {
        boolean result = false;

        Card weapon = null;
        Card suspect = null;
        Card room = null;

        Player me = gameController.getBoard().getPlayer(myName);
        CardCollection myCards = me.getCards();
        CardCollection myDCards = me.getDetectivePad().getCardList();

        int numberOfWeapons = 0;
        int numberOfRooms = 0;
        int numberOfSuspects = 0;

        ArrayList cards = new ArrayList();

        for (int i = 0; i < myDCards.countCards(); i++) {
            if (myDCards.cardAt(i).isEliminated() == false) {
                if (myDCards.cardAt(i).getType().equals("Weapon"))
                    numberOfWeapons++;
                else if (myDCards.cardAt(i).getType().equals("Suspect"))
                    numberOfSuspects++;
                else if (myDCards.cardAt(i).getType().equals("Room"))
                    numberOfRooms++;

                cards.add(myDCards.cardAt(i));
            }
        }

        System.out.println("\n\n\n\nshouldAccuse: " + numberOfWeapons + " " + numberOfSuspects
                + " " + numberOfRooms + "\n\n\n\n");

        for (int i = 0; i < cards.size(); i++)
            System.out.println(((Card) cards.get(i)).getName());

        result = (numberOfWeapons == 1 && numberOfSuspects == 1 && numberOfRooms == 1);

        return result;
    }

    /**
     * Find 3 cards to use in an accusation then call the accuse() function.
     * 
     */
    synchronized public boolean makeAccusation() {
        Card weapon = null;
        Card suspect = null;
        Card room = null;

        Player me = gameController.getBoard().getPlayer(myName);
        CardCollection myCards = me.getCards();
        CardCollection myDCards = me.getDetectivePad().getCardList();

        for (int i = 0; i < myDCards.countCards(); i++) {
            if (myDCards.cardAt(i).isEliminated() == false) {
                if (weapon == null && myDCards.cardAt(i).getType().equals("Weapon"))
                    weapon = myDCards.cardAt(i);
                else if (weapon == null && myDCards.cardAt(i).getType().equals("Suspect"))
                    suspect = myDCards.cardAt(i);
                else if (room == null && myDCards.cardAt(i).getType().equals("Room"))
                    room = myDCards.cardAt(i);
            }
        }

        return accuseCorrect(suspect, weapon, room);
    }

    /**
     * Check if an accusation is correct
     */
    synchronized public boolean accuseCorrect(Card suspectCard, Card weaponCard, Card roomCard) {
        boolean result = false;

        Player currentPlayer = gameController.getBoard().getCurrentPlayer();

        System.out.println("accuseCorrect " + suspectCard.getName() + " " + weaponCard.getName()
                + " " + roomCard.getName());

        result = gameController.getBoard().getEnvelope().containsCard(weaponCard) == true &&
                gameController.getBoard().getEnvelope().containsCard(roomCard) == true &&
                gameController.getBoard().getEnvelope().containsCard(suspectCard) == true;

        try {
            System.out.println("accuseCorrect " + result + "\n envelope size "
                    + gameController.getBoard().getEnvelope().countCards() + " xml: "
                    + gameController.getBoard().getEnvelope().toXML());
        } catch (Exception e) {
            System.out.println("accuseCorrect " + result + "\n envelope  NULL ?! "
                    + gameController.getBoard().getEnvelope());
        }
        return result;
    }

    // Peter: SUGGESTION.
    // Peter: We should use a probability in this function.
    public String suggest()
    {
        Card suspect = null;
        Card weapon = null;
        String output = "";

        Player me = gameController.getBoard().getPlayer(myName);
        CardCollection myDCards = me.getDetectivePad().getCardList();

        boolean askDisproof = true;

        /** List of all suspects who haven't been eliminated yet **/
        Vector possSuspects = new Vector();
        /** List of all weapons which haven't been eliminated yet **/
        Vector possWeapons = new Vector();

        /**
         * Search through detective pad to find cards which aren't eliminated.
         */
        // PETER: Codes below choose a card in randomly.
        // PETER: We should use a probability in this function.
        // PETER: Use the method I made. Eg) myDCards.getEliminatedCardsWithLowProb();
        // PETER: Use the method I made. Eg) myDCards.getEliminatedCardsWithHighProb();
        // PETER: Use the method I made. Eg) myDCards.getUneliminatedCardsWithLowProb();
        // PETER: Use the method I made. Eg) myDCards.getUneliminatedCardsWithHighProb();
        // PETER: Use the method I made. Eg) myDCards.cardAt(i).getProbability();

        /*
         * for(int i = 0; i < myDCards.countCards(); i++)
         * if(myDCards.cardAt(i).getType().equals("Suspect") && myDCards.cardAt(i).isEliminated() ==
         * false) possSuspects.add(myDCards.cardAt(i)); myDCards.getEliminatedCardsWithLowProb();
         * 
         * // Search through detective pad to find cards which aren't eliminated. for(int i = 0; i <
         * myDCards.countCards(); i++) if(myDCards.cardAt(i).getType().equals("Weapon") &&
         * myDCards.cardAt(i).isEliminated() == false) possWeapons.add(myDCards.cardAt(i));
         * 
         * // Generate a random number then use that index to access the list of suspects. suspect =
         * (Card)possSuspects.elementAt( (int) (Math.random() * possSuspects.size())) ; // Generate
         * a random number then use that index to access the list of weapons. weapon =
         * (Card)possWeapons.elementAt( (int) (Math.random() * possWeapons.size())) ;
         */

        suspect = myDCards.getUneliminatedCardsWithLowProb("Suspect");
        weapon = myDCards.getUneliminatedCardsWithLowProb("Weapon");

        // Get the room for the suggestion by the room the player is currently standing in.
        Card room = gameController.getBoard().getCardPack().cardByName(me.getPosition().getID());

        Vector disproofInfo = findDisproveCandidate(suspect, weapon, room);

        Player playerCandidate = (Player) disproofInfo.firstElement();

        if (possSuspects != null)
            output += "- suspects " + possSuspects.size() + " weapons: " + possWeapons.size()
                    + " pos " + me.getPosition().getID() + ".\n";
        else
            output += "- suspects " + "NULL" + " weapons: " + "NULL" + " pos "
                    + me.getPosition().getID() + ".\n";
        if (suspect != null)
            output += "- I suspect " + suspect.getName();
        else
            output += "- I suspect " + "NULL";
        if (weapon != null)
            output += " of using the " + weapon.getName();
        else
            output += " of using the " + "NULL";

        output += " in the " + me.getPosition().getID() + ".\n";
        output += "Candidate found: " + playerCandidate;
        outputMessage(output);

        if (suspect != null)
            moveDefendant(suspect);
        System.out.println("PlyrCalled " + myName + " into the " + room.getName() + " "
                + suspect.getName());
        // Send out an alert for the suggestion
        // Peter: It sends the suggestion message to the thread pipeline.
        if (playerCandidate != null)
        {
            disproofReceived = false;
            Message suggestionAlertMessage = new Message();
            Vector params = new Vector();
            // params.add( new SuggestionAlert(suspect, weapon, room, me.getRealName(),
            // playerCandidate.getRealName()) );
            params.add(new SuggestionAlert(suspect, weapon, room, me.getRealName()));
            suggestionAlertMessage.parameters = params;
            suggestionAlertMessage.type = "suggestionAlert";
            suggestionAlertMessage.data = playerCandidate.getRealName();

            gameController.relayMessage(suggestionAlertMessage);
        }
        else
        {
            outputMessage("Nobody could disprove");
            outputMessage(" i have weap? " + myDCards.containsCard(weapon) + "\n i have room? "
                    + myDCards.containsCard(room) + "\n i have suspect? "
                    + myDCards.containsCard(suspect));
            askDisproof = false;
        }

        String cardShown = "";

        if (askDisproof == true)
        {
            // Wait for a reply from the suggestion
            // Peter: In this routine, this thread will be stalled until the result comes.
            while (disproofReceived == false)
            {
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            outputMessage("Start disproof decomposition");

            DisproofAlert disproofAlert = (DisproofAlert) incomingDisproof.parameters
                    .firstElement();
            SuggestionAlert originalSuggestionAlert = disproofAlert.getOriginalSuggestion();

            cardShown = disproofAlert.getCardShown();

            System.out.println(" - Card shown: " + cardShown);
        }
        else {
            cardShown = "";
        }
        // Figure out what to do after the suggestion
        /** A card was shown to us */
        if (!cardShown.equals(""))
        {
            outputMessage("I was shown a card for my suggestion, " + cardShown);

            // We got a good response from the suggestion, one card has been
            // eliminated.
            // TODO eliminate card means we confirmed it
            DisproofAlert disproofAlert = (DisproofAlert) incomingDisproof.parameters
                    .firstElement();

            updatePlayer(disproofAlert);
            eliminateCard(myDCards.cardByName(cardShown));
            System.out.println("Card marked");

            // Peter: In this routine, we should change probabilities.
            // Peter: Use the method I made. Eg) myDCards.cardAt(i).setProbability(DOUBLE NUMBER);

        }
        // Peter: No one has my suggested cards. So, this is the answer. The assumption is that I
        // don't suggest one of my cards.
        // Peter: If we suggests one of our cards, then this routine MUST be changed.
        /** No card was shown to us */
        else
        {
            if (me.getCards().containsCard(suspect) == false
                    && me.getCards().containsCard(weapon) == false
                    && me.getCards().containsCard(room) == false)
            {
                output += "\nI secretly won because I know nobody else has the cards.\n";
                System.out.println(output);

                // At this stage we should check if we have the cards.. if not..
                // then we must know that we've found the correct cards.
                if (accuseCorrect(suspect, weapon, room) == true)
                {
                    outputMessage("The accusation made was correct");
                    Vector parameters = new Vector();
                    parameters.add(me);
                    gameController.directCommand("winGame", parameters);
                }
                else
                {
                    outputMessage("The accusation made was WRONG..!");
                    dead = true;
                }
            }
            else
                output += "Unable to disprove suggestion because I had all the cards myself.";
        }

        /** So we can't suggest next turn */
        markSuggestionMade();
        return output;
    }

    public void markSuggestionMade() {
        Vector param = new Vector();
        param = new Vector();
        param.add(gameController.getBoard().getPlayer(myName));
        gameController.directCommand("markSuggestMade", param);
    }

    public void eliminateCard(Card shown) {
        Vector param = new Vector();
        param.add(shown.getName());
        gameController.directCommand("eliminateCard", param);
    }

    /**
     * Performs the move of the player from current position to "target".
     * 
     */
    synchronized public void movePlayer(Position target, Vector path) {
        String output = "";
        Vector pathTaken = applyConstraint(target, path);
        System.out.println("Feed forward : " + pathTaken);

        Player me = gameController.getBoard().getPlayer(myName);
        output = "#movePlayer Moving to " + target.getID();
        me.setMovesRemaining(0);
        Vector param = new Vector();
        param.add(target);
        param.add(me);
        param.add(pathTaken);
        gameController.directCommand("movePlayer", param);

        outputMessage(output);
    }

    /**
     * 
     * Applies constraints to the path
     * 
     * @param target
     * @param path
     * @return a new path with the constraints applied
     */
    private Vector applyConstraint(Position target, Vector path) {
        Vector altPath = new Vector();
        for (int i = 0; i < path.size(); i++) {
            if (path.elementAt(i) == target) {
                altPath.add(path.elementAt(i));
                break;
            }
            else {
                altPath.add(path.elementAt(i));
            }
        }
        return altPath;
    }

    /**
     * Rolls the dice and then gets the result.
     * 
     * @return number of both dice
     */
    public int rollDice() throws Exception {
        int result = 0;

        Player me = gameController.getBoard().getPlayer(myName);
        System.out.println("Sending command");
        gameController.directCommand("roll", new Vector());

        System.out.println("Command sent");
//        result = me.getMovesRemaining();
        result = 10000;
        System.out.println("Result: " + result);

        return result;
    }

    /**
     * Find a player for a disproof
     * <p>
     *
     * Returns Vector
     * <p>
     * <li>- first element -- pointer to player with cards
     * <li>- second element -- Vector of cards for disprove
     * <li>- elements -- each Card
     */
    public Vector findDisproveCandidate(Card who, Card what, Card where) {
        Vector results = new Vector();

        String output = "";
        Player playerWithCard = null;
        Vector cardsFound = new Vector();

        Player currentPlayer = gameController.getBoard().getCurrentPlayer();

        // Call who into where.

        Player defendant = gameController.getBoard().getCharacter(who.getName());

        if (!currentPlayer.getCharacter().equals(who.getName())) {
            if (defendant != null) {
                Position oldRoom = defendant.getPosition();
                Position destination = currentPlayer.getPosition();

                Vector parameters = new Vector();
                parameters.add(destination);
                parameters.add(defendant); // Who to move..
                gameController.directCommand("movePlayer", parameters);
            }
        }

        // Search all players for a card.

        playerWithCard = null;
        cardsFound = new Vector();

        // Order the players so we can scan through and check who has cards in the right order.
        Vector playersToParse = localArrangePlayersBy(currentPlayer);

        for (int i = 0; i < playersToParse.size(); i++) {
            playerWithCard = (Player) playersToParse.elementAt(i);
            if (playerWithCard != currentPlayer) {
                if (playerWithCard.hasCard(who))
                    cardsFound.add(who);
                if (playerWithCard.hasCard(what))
                    cardsFound.add(what);
                if (playerWithCard.hasCard(where))
                    cardsFound.add(where);
                if (cardsFound.size() > 0)
                    break;
            }
        }

        if (cardsFound.size() > 0) {
            output += playerWithCard.getRealName()
                    + " choose which card you will use to disprove :-\n ";
            for (int i = 0; i < cardsFound.size(); i++) {
                output += ((Card) cardsFound.elementAt(i)).getName() + " ";
            }
            // gameConfig.setDisproveMode(true);
        }
        else {
            output += "Unable to disprove the suggestion.";
            playerWithCard = null;
        }

        outputMessage(output);

        results.add(playerWithCard);
        results.add(cardsFound);

        return results;
    }

    /**
     * Order the players so we can scan through and check who has cards in the right order.
     * 
     */
    private Vector localArrangePlayersBy(Player currentPlayer) {
        Vector players = new Vector();
        int playerPosition = gameController.getBoard().getPlayerIndex(currentPlayer);
        Vector livePlayers = gameController.getBoard().getPlayers();

        // Add all elements after the player
        for (int i = playerPosition + 1; i < livePlayers.size(); i++)
            players.add(livePlayers.elementAt(i));
        // Add all elements before the player
        if (!(playerPosition == 0))
            for (int i = 0; i < playerPosition; i++)
                players.add(livePlayers.elementAt(i));
        return players;

    }

    /**
     * Checks if a suggest was disproved or not with a given suspect and weapon.
     * 
     * @parameter suspect the suspect to eliminate (who did it)
     * @parameter suspect the weapon to eliminate (what did it)
     * 
     * @return false - unable to disprove, true - disproved.
     */
    public void moveDefendant(Card suspect) {
        Player defendant = gameController.getBoard().getCharacter(suspect.getName());
        Player me = gameController.getBoard().getPlayer(myName);

        Card room = gameController.getBoard().getCardPack().cardByName(me.getPosition().getID());
        if (defendant != null) {
            String output = "";

            if (defendant != null) {
                Position oldRoom = defendant.getPosition();
                Position destination = me.getPosition();

                Vector parameters = new Vector();
                parameters.add(destination); // Where to move
                parameters.add(defendant); // Who to move there
                gameController.directCommand("movePlayer", parameters);
            }

            output = "Called " + defendant.getRealName() + " into the " + room.getName();

            outputMessage(output);
        }
    }

    public boolean inDeadlock() {
        Position myPosition = gameController.getBoard().getCurrentPlayer().getPosition();
        Vector N = myPosition.getNeighbours();
        int free = 0;

        for (int i = 0; i < N.size(); i++) {
            Position n = (Position) (N.get(i));
            if (n instanceof Square || n instanceof StartingSquare) {
                if (n.isOccupied() == false) {
                    free++;
                }
            }
            else {
                free++;
            }
        }
        return (free == 0);
    }

    /*
     * (non-Javadoc)
     * 
     * @see Cluedo.API.AIContract#movePlayer(Cluedo.Game.Position, java.util.Vector, int)
     */
    public void movePlayer(Position target, Vector path, int amount) {

    }

    private void updatePlayerSoftEvidence(DisproofAlert disproofAlert,
            String disprovedBy) {
        if (playerMapping.get(disproofAlert.originalSuggestionAlert.getSuggestedBy())
                    != playerMapping.get(myName) &&
                playerMapping.get(disproofAlert.disprovedBy)
                    != playerMapping.get(myName)) {
            cDataPad.updatePlayerSoftEvidence(
                    playerMapping.get(disproofAlert.disprovedBy),
                    playerMapping.get(disproofAlert.originalSuggestionAlert.getSuggestedBy()),
                    new String[] {
                            disproofAlert.originalSuggestionAlert.what.getName(),
                            disproofAlert.originalSuggestionAlert.who.getName(),
                            disproofAlert.originalSuggestionAlert.where.getName()
                    });
            updateRegistryData(me);
        }
    }

    private void updatePlayer(DisproofAlert disproofAlert) {
        cDataPad.updatePlayer(
                playerMapping.get(myName),
                playerMapping.get(disproofAlert.disprovedBy),
                new String[] {
                        disproofAlert.originalSuggestionAlert.who.getName(),
                        disproofAlert.originalSuggestionAlert.what.getName(),
                        disproofAlert.originalSuggestionAlert.where.getName()
                },
                disproofAlert.cardShown);
        updateRegistryData(me);
    }

    private void updateRegistryData(Player me) {
        cDataPad.narrowPlayerStatistics();
        for (int i = 0; i <= CluedoDataPad.getTokens().size(); i++) {
            Card card = me.getDetectivePad().getCardList().cardAt(i);
            if (card != null) {
                int tokCol = CluedoDataPad.getTokens().get(card.getName());
                card.setProbability(cDataPad.getGrid()[cDataPad.getGrid().length - 1][tokCol]);
            }
        }
    }
}
